﻿using AsmShaderConverter.AsmFunctions;
using AsmShaderConverter.AsmShader;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;

namespace TestAsmConverter
{
    [TestClass]
    public class AsmFunctions
    {
        private static Dictionary<string, AsmShaderConverter.AsmShader.ExportRegisterNames> exportRegisterNames = new Dictionary<string, AsmShaderConverter.AsmShader.ExportRegisterNames>();

        [TestMethod]
        public void TestMethodAdd()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "add o2, r1, c48"
            }
            ;

            AsmFunctionAdd madFunction = new AsmFunctionAdd(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xyzw = r1.xyzw + c48.xyzw;");
        }

        [TestMethod]
        public void TestMethodAdd2()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "add o2.xy, r1, c48"
            }
            ;

            AsmFunctionAdd madFunction = new AsmFunctionAdd(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xy = r1.xy + c48.xy;");
        }

        [TestMethod]
        public void TestMethodLrp()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "lrp r5.xyz, c23.w, r0.y, r3"
            }
            ;

            AsmFunctionLrp madFunction = new AsmFunctionLrp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r5.xyz = lerp(r3.xyz, r0.y, c23.w);");
        }

        [TestMethod]
        public void TestMethodLrp2()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "lrp r2.yzw, c24.w, r1.w, r3.xxyz"
            }
            ;

            AsmFunctionLrp madFunction = new AsmFunctionLrp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r2.yzw = lerp(r3.xyz, r1.w, c24.w);");
        }

        [TestMethod]
        public void TestMethodMax()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "max o2, r1, c48"
            }
            ;

            AsmFunctionMax madFunction = new AsmFunctionMax(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xyzw = max(r1.xyzw, c48.xyzw);");
        }

        [TestMethod]
        public void TestMethodCmp()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "cmp oC0.x, -r0.x, c37.x, r1.x" ,
                "cmp oC0.yzw, -r0.x, c36, r1",
                "cmp r2, -r2_abs.x, r5, c40"
            }
            ;

            AsmFunctionCmp madFunction = new AsmFunctionCmp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "oC0.x = (-r0.x < 0 ? c37.x : r1.x);\r\n");

            madFunction = new AsmFunctionCmp(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "oC0.yzw = float3(-r0.x < 0 ? c36.y : r1.y,\n-r0.x < 0 ? c36.z : r1.z,\n-r0.x < 0 ? c36.w : r1.w);\r\n");

            madFunction = new AsmFunctionCmp(lineOfCode[2]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r2.xyzw = float4(-abs(r2.x) < 0 ? r5.x : c40.x,\n-abs(r2.x) < 0 ? r5.y : c40.y,\n-abs(r2.x) < 0 ? r5.z : c40.z,\n-abs(r2.x) < 0 ? r5.w : c40.w);\r\n");
        }

        [TestMethod]
        public void TestMethodMin()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "max o2, r1, c48"
            }
            ;

            AsmFunctionMin madFunction = new AsmFunctionMin(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xyzw = min(r1.xyzw, c48.xyzw);");
        }

        [TestMethod]
        public void TestMethodAddSat()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "add_sat_pp o2, -r1, c48"
            }
            ;

            AsmFunctionAddSatPP madFunction = new AsmFunctionAddSatPP(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xyzw = saturate(-r1.xyzw + c48.xyzw);");
            Assert.AreEqual(madFunction.destination.halfPrecison, true);
        }

        [TestMethod]
        public void TestMethodAddSatPP()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "add_sat_pp o2, r1, c48"
            }
            ;

            AsmFunctionAddSatPP madFunction = new AsmFunctionAddSatPP(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o2.xyzw = saturate(r1.xyzw + c48.xyzw);");
        }

        [TestMethod]
        public void TestMethodDot2add()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp2add r0.w, r1.x, r0.w, c0.w" ,
                "dp2add r0.w, r1.xy, r0.w, c0.w"
            }
            ;

            AsmFunctionDot2add madFunction = new AsmFunctionDot2add(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = dot(float2(r1.x, r1.x), float2(r0.w, r0.w)) + c0.w;");

            madFunction = new AsmFunctionDot2add(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = dot(r1.xy, float2(r0.w, r0.w)) + c0.w;");
        }

        [TestMethod]
        public void TestMethodDot2addPP()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp2add_pp r0.w, r1.x, r0.w, c0.w" ,
                "dp2add_pp r0.w, r1.xy, r0.w, c0.w"
            }
            ;

            AsmFunctionDot2addPP madFunction = new AsmFunctionDot2addPP(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(madFunction.destination.halfPrecison, true);

            Assert.AreEqual(hlsl, "r0.w = dot(float2(r1.x, r1.x), float2(r0.w, r0.w)) + c0.w;");

            madFunction = new AsmFunctionDot2addPP(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(madFunction.destination.halfPrecison, true);
            Assert.AreEqual(hlsl, "r0.w = dot(r1.xy, float2(r0.w, r0.w)) + c0.w;");
        }

        [TestMethod]
        public void TestMethodPow()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "pow r1.x, r0.x, c4.x"
            }
            ;

            AsmFunctionPow madFunction = new AsmFunctionPow(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.x = pow(r0.x, c4.x);");
        }

        [TestMethod]
        public void TestMethodDot3()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp3 r0.w, r1, r1"
            }
            ;

            AsmFunctionDot3 madFunction = new AsmFunctionDot3(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = dot(r1.xyz, r1.xyz);");
        }

        [TestMethod]
        public void TestMethodDot3Sat()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp3_sat r0.w, r0, c14"
            }
            ;

            AsmFunctionDot3Sat madFunction = new AsmFunctionDot3Sat(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = saturate(dot(r0.xyz, c14.xyz));");
        }

        [TestMethod]
        public void TestMethodDot3PP()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp3_pp r1.x, r0, r1"
            }
            ;

            AsmFunctionDot3PP madFunction = new AsmFunctionDot3PP(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.x = dot(r0.xyz, r1.xyz);");
            Assert.AreEqual(madFunction.destination.halfPrecison, true);
        }

        [TestMethod]
        public void TestMethodDot4()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "dp4 o0.x, r0, c20",
                "dp4 o0.x, r0.x, c20"       // wrong swizzle at r0.x
            }
            ;

            AsmFunctionDot4 madFunction = new AsmFunctionDot4(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o0.x = dot(r0.xyzw, c20.xyzw);");

            // Hier sollte es schiefgehen
            try
            {
                madFunction = new AsmFunctionDot4(lineOfCode[1]);
                hlsl = madFunction.ToHLSL(asm);
                Assert.Fail("Should be failed");
            }
            catch (Exception)
            {
                Console.WriteLine("it is correct to throw an exception here!!");
            }
        }

        [TestMethod]
        public void TestMethodEndIf()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "endif"
            }
            ;

            AsmFunctionEndif madFunction = new AsmFunctionEndif(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "}");
        }

        [TestMethod]
        public void TestMethodIf()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "if b4"
            }
            ;

            AsmFunctionIf madFunction = new AsmFunctionIf(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "if (b4)\n{");
        }

        [TestMethod]
        public void TestMethodElse()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "else"
            }
            ;

            AsmFunctionElse madFunction = new AsmFunctionElse(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "}\nelse\n{");
        }

        [TestMethod]
        public void TestMethodMad()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mad r0.xyz, v1, c0.x, c0.y",
                "mad r0.xyz, v1.xxx, c0.x, c0.y",
            }
                ;

            AsmFunctionMad madFunction = new AsmFunctionMad(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.xyz = v1.xyz * c0.x +  c0.y;");

            madFunction = new AsmFunctionMad(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.xyz = v1.xxx * c0.x +  c0.y;");
        }

        [TestMethod]
        public void TestMethodMad2()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string lineOfCode = "mad r1.zw, vPos.xyxy, c20.zwzw, c20";

            AsmFunctionMad madFunction = new AsmFunctionMad(lineOfCode);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.zw = vPos.xy * c20.zw +  c20.zw;");
        }

        [TestMethod]
        public void TestMethodMadSat()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mad_sat r0.xyz, v1, c0.x, c0.y",
                "mad_sat r0.xyz, v1.xxx, c0.x, c0.y",
            }
                ;

            AsmFunctionMadSat madFunction = new AsmFunctionMadSat(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.xyz = saturate(v1.xyz * c0.x +  c0.y);");

            madFunction = new AsmFunctionMadSat(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.xyz = saturate(v1.xxx * c0.x +  c0.y);");
        }

        [TestMethod]
        public void TestMethodMadSat2()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mad_sat r1.xyz, v0.xyww, c0.x, c0.y",
            }
                ;

            AsmFunctionMadSat madFunction = new AsmFunctionMadSat(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.xyz = saturate(v0.xyw * c0.x +  c0.y);");
        }

        [TestMethod]
        public void TestMethodMov()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mov o5.xy, r2" ,
                "mov o5.xy, -r2" ,
            }
            ;

            AsmFunctionMov madFunction = new AsmFunctionMov(lineOfCode[0], false, false);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o5.xy = r2.xy;");

            madFunction = new AsmFunctionMov(lineOfCode[1], false, false);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "o5.xy = -r2.xy;");
        }

        [TestMethod]
        public void TestMethodMul()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mul r1.xyz, r1, c30"
            }
            ;

            AsmFunctionMul madFunction = new AsmFunctionMul(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.xyz = r1.xyz * c30.xyz;");
        }

        [TestMethod]
        public void TestMethodMulSat()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mul r1.xyz, r1, c30"
            }
            ;

            AsmFunctionMulSat madFunction = new AsmFunctionMulSat(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.xyz = saturate(r1.xyz * c30.xyz);");
        }

        [TestMethod]
        public void TestMethodMulPP()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "mul_pp r1.xyz, r1.x, v3"
            }
            ;

            AsmFunctionMulPP madFunction = new AsmFunctionMulPP(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r1.xyz = r1.x * v3.xyz;");
            Assert.AreEqual(madFunction.destination.halfPrecison, true);
        }

        [TestMethod]
        public void TestMethodNrm()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "nrm r0.xyz, v2"
            }
            ;

            AsmFunctionNrm madFunction = new AsmFunctionNrm(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.xyz = normalize(v2.xyz);");
        }

        [TestMethod]
        public void TestMethodRcp()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "rsq r0.w, r0.w"
            }
            ;

            AsmFunctionRcp madFunction = new AsmFunctionRcp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = 1.0f / r0.w;");
        }

        [TestMethod]
        public void TestMethodRsq()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "rsq r0.w, r0.w"
            }
            ;

            AsmFunctionRsq madFunction = new AsmFunctionRsq(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = rsqrt(r0.w);");
        }

        [TestMethod]
        public void TestMethodFrc()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "frc r0.w, r0.w"
            }
            ;

            AsmFunctionFrc madFunction = new AsmFunctionFrc(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = frac(r0.w);");
        }

        [TestMethod]
        public void TestMethodLog()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "log r0.w, r0.w"
            }
            ;

            AsmFunctionLog madFunction = new AsmFunctionLog(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r0.w = log(r0.w);");
        }

        [TestMethod]
        public void TestMethodTexkill()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "texkill r2"
            }
            ;

            AsmFunctionTexkill madFunction = new AsmFunctionTexkill(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "clip(r2.xyzw);");
        }

        [TestMethod]
        public void TestMethodTexldr()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "texld r2, r1.zwzw, s15"
            }
            ;

            AsmFunctionTexldr madFunction = new AsmFunctionTexldr(lineOfCode[0], false);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r2.xyzw = noneRegistered.Sample(s15, r1.zw);");
        }

        [TestMethod]
        public void TestMethodTexldp()
        {
            AsmShaderFile asm = new AsmShaderFile();
            asm.AddExportRegister(new ExportRegisterNames() { ExportName = "Samplername", Length = 1, offset = 0, registerName = "s15", typeName = "sampler2D" });

            asm.AddSamplerToTexture(new Sampler2Texture() { samplerName = "s15", textureName = "Tex1", samplerState = EnumSamplerstate.Texture2D });
            string textureName = "TexDiffuse";

            string[] lineOfCode = new string[]{
                "texld r2, r1, s15"
            }
            ;

            AsmFunctionTexldp madFunction = new AsmFunctionTexldp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            // DX9 style:Assert.AreEqual(hlsl, "r2 = tex2Dproj(s15, r1.xyzw);");
            Assert.AreEqual(hlsl, "r2.xyzw = Tex1.Sample(Samplername, r1.xy / r1.w);");
        }

        [TestMethod]
        public void TestMethodTexldp2()
        {
            AsmShaderFile asm = new AsmShaderFile();
            asm.AddExportRegister(new ExportRegisterNames() { ExportName = "sampler15", offset = 0, registerName = "s15", Length = 1, typeName = "sampler" });
            asm.AddSamplerToTexture(new Sampler2Texture() { samplerName = "s15", textureName = "Texture15", samplerState = EnumSamplerstate.Texture2D });

            string[] lineOfCode = new string[]{
                "texld r2, r1, s15"
            }
            ;

            AsmFunctionTexldp madFunction = new AsmFunctionTexldp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            // DX9 style:Assert.AreEqual(hlsl, "r2 = tex2Dproj(s15, r1.xyzw);");
            Assert.AreEqual(hlsl, "r2.xyzw = Texture15.Sample(sampler15, r1.xy / r1.w);");
        }

        [TestMethod]
        public void TestMethodTexldCubeSampler()
        {
            AsmShaderFile asm = new AsmShaderFile();
            asm.AddExportRegister(new ExportRegisterNames() { ExportName = "sampler15Cube", offset = 0, registerName = "s15", Length = 1, typeName = "sampler" });
            asm.AddSamplerToTexture(new Sampler2Texture() { samplerName = "s15", textureName = "Texture15", samplerState = EnumSamplerstate.TextureCube });

            string[] lineOfCode = new string[]{
                "texld_pp r2, r1, s15"
            }
            ;

            AsmFunctionTexldr madFunction = new AsmFunctionTexldr(lineOfCode[0], true);
            string hlsl = madFunction.ToHLSL(asm);

            // DX9 style:Assert.AreEqual(hlsl, "r2 = tex2Dproj(s15, r1.xyzw);");
            Assert.AreEqual(hlsl, "r2.xyzw = Texture15.Sample(sampler15Cube, r1.xyz);");
        }

        [TestMethod]
        public void TestMethodMadLrp()
        {
            AsmShaderFile asm = new AsmShaderFile();
            string[] lineOfCode = new string[]{
                "lrp r5.xyz, c23.w, r0.x, r3",
               "lrp r3, r1.x, r5, r4",
            }
                ;

            AsmFunctionLrp madFunction = new AsmFunctionLrp(lineOfCode[0]);
            string hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r5.xyz = lerp(r3.xyz, r0.x, c23.w);");

            madFunction = new AsmFunctionLrp(lineOfCode[1]);
            hlsl = madFunction.ToHLSL(asm);

            Assert.AreEqual(hlsl, "r3.xyzw = lerp(r4.xyzw, r5.xyzw, r1.x);");
        }
    }
}